/*
    日志格式化类：
    按照格式控制字符串，组织日志消息类的各项要素，组成日志消息字符串并输出
*/

#pragma once

#include "util.hpp"
#include "level.hpp"
#include "message.hpp"
#include "except.hpp"
#include <iostream>
#include <sstream>
#include <vector>
#include <ctime>
#include <cassert>

namespace easylog
{
    //格式化基类
    class FormatItem{
        public:
            using ptr = std::shared_ptr<FormatItem>;//父类指针
            virtual void format(std::ostream& out, LogMsg& msg) = 0;
    };
    //时间格式化
    class TimeFormatItem : public FormatItem{
        public:
            TimeFormatItem(const std::string& fmt = "%F %H:%M:%S"):_fmt(fmt){}
            void format(std::ostream& out, LogMsg& msg) override{
                struct tm t;
                localtime_r(&msg._ctime, &t);//将时间戳转换成时间结构体
                char tmp[32] = {0};
                strftime(tmp, 31, _fmt.c_str(), &t);
                out << tmp;
            }
        private:
            std::string _fmt;//时间格式 默认为%F %H:%M:%S
    };
    //日志器格式化
    class LoggerFormatItem : public FormatItem{
        public:
            void format(std::ostream& out, LogMsg& msg) override{
                out << msg._logger;
            }
    };
    //线程ID格式化
    class ThreadFormatItem : public FormatItem{
        public:
            void format(std::ostream& out, LogMsg& msg) override{
                out << msg._tid;
            }
    };
    //源码文件名格式化
    class FileFormatItem : public FormatItem{
        public:
            void format(std::ostream& out, LogMsg& msg) override{
                out << msg._file;
            }
    };
    //源码行号名格式化
    class LineFormatItem : public FormatItem{
        public:
            void format(std::ostream& out, LogMsg& msg) override{
                out << msg._line;
            }
    };
    //日志等级名格式化
    class LevelFormatItem : public FormatItem{
        public:
            void format(std::ostream& out, LogMsg& msg) override{
                out << LogLevel::toString(msg._level);
            }
    };
    //消息主体名格式化
    class MsgFormatItem : public FormatItem{
        public:
            void format(std::ostream& out, LogMsg& msg) override{
                out << msg._payload;
            }
    };
    //换行符格式化
    class NLineFormatItem : public FormatItem{
        public:
            void format(std::ostream& out, LogMsg& msg) override{
                out << "\n";
            }
    };
    //制表符格式化
    class TabFormatItem : public FormatItem{
        public:
            void format(std::ostream& out, LogMsg& msg) override{
                out << "\t";
            }
    };
    //其他符号格式化
    class OtherFormatItem : public FormatItem{
        public:
            OtherFormatItem(const std::string& str):_str(str){}
            void format(std::ostream& out, LogMsg& msg) override{
                out << _str;
            }
        private:
            std::string _str;
    };


    //格式化器
    class Formatter
    {
    public:
        using ptr = std::shared_ptr<Formatter>;
        Formatter(const std::string& pattern = "[%d{%F:%H:%M:%S}][%c][%t][%f:%l][%p]%T%m%n")
        :_pattern(pattern)
        {
            assert(parsePattern());//保证解析格式控制串必须成功
        }
        //日志输出
        void format(std::ostream& out, LogMsg& msg)
        {
            for(auto& item : _items)
                item->format(out, msg);
        }
        std::string format(LogMsg& msg)
        {
            std::stringstream ss;
            format(ss, msg);
            return ss.str();
        }
        
    private:
        //解析格式控制字符串
        bool parsePattern()
        {
            //逐个遍历，不是%就是其他字符
            //遇到%，先判断后面是不是%,可能出现%%的情况，此时也是其他字符
            //其次获取格式字符
            //再判断后面是否有{,有则代表有子格式

            std::string val;
            int n = _pattern.size(), i = 0;
        
            while(i < n)
            {
                if(_pattern[i] != '%')
                {
                    val += _pattern[i++]; 
                    continue;
                }
                //走到这说明是%
                if(val.size() > 0)
                {
                    //看前面是否有其他字符
                    _items.push_back(createItem("", val));
                    val.clear();
                }
                //两个%%
                if(i + 1 < n && _pattern[i + 1] == '%')
                {
                    _items.push_back(createItem("", "%"));
                    i += 2;
                    continue;
                }
                if(i + 1 >= n)//不符合格式，%后应该有字符
                    throw except("%后没有格式化字符");
                std::string key;
                //格式字符
                key = _pattern[i + 1];
                i += 2;
                //有子格式
                if(i < n && _pattern[i] == '{')
                {
                    size_t pos = _pattern.find('}', i);
                    if(pos == std::string::npos)
                        throw except("子格式{}匹配失败");
                    val = _pattern.substr(i + 1, pos - i - 1);
                    i = pos + 1;
                }
                _items.push_back(createItem(key, val));
                val.clear();//清空val
            }
            return true;
        }


        //根据不同格式字符，创建不同的格式化子项对象
        FormatItem::ptr createItem(const std::string& key, const std::string& val)
        {
            if(key == "d") return std::make_shared<TimeFormatItem>(val);
            if(key == "t") return std::make_shared<ThreadFormatItem>();
            if(key == "p") return std::make_shared<LevelFormatItem>();
            if(key == "c") return std::make_shared<LoggerFormatItem>();
            if(key == "f") return std::make_shared<FileFormatItem>();
            if(key == "l") return std::make_shared<LineFormatItem>();
            if(key == "m") return std::make_shared<MsgFormatItem>();
            if(key == "n") return std::make_shared<NLineFormatItem>();
            if(key == "T") return std::make_shared<TabFormatItem>();
            if(key.empty()) return std::make_shared<OtherFormatItem>(val);
            throw except("没有对应的格式化字符：%" + val);
            abort();
            return FormatItem::ptr();
        }
    private:
        std::string _pattern;//格式化控制串
        std::vector<FormatItem::ptr> _items;//格式化子项数组
    };
}
